﻿using System;
using System.Collections.Generic;
using System.Linq;
using SharpShare.Storage;
using SharpShare.Storage.Searching.Parameters;
using SharpShare.Storage.Security;

namespace SharpShare.Afp.Protocol {
    internal static class StorageExtensions {
        public static AfpResultCode SetStorageItemParameters(this IStorageItem item, IAfpVolume volume, AfpFileDirectoryBitmap bitmap, AfpStream stream) {
            IStorageContainer container = item.AsContainer();

            foreach (AfpFileDirectoryBitmap flag in bitmap.EnumerateFlags()) {
                switch (flag) {
                    case AfpFileDirectoryBitmap.kFPModDateBit:
                        try {
                            item.DateModified = stream.ReadMacintoshDate();
                        } catch { }
                        break;
                    case AfpFileDirectoryBitmap.kFPCreateDateBit:
                        try {
                            item.DateCreated = stream.ReadMacintoshDate();
                        } catch { }
                        break;
                    case AfpFileDirectoryBitmap.kFPBackupDateBit:
                        try {
                            item.SetBackupDate(stream.ReadMacintoshDate());
                        } catch { }
                        break;
                    case AfpFileDirectoryBitmap.kFPAttributeBit: {
                            AfpFileDirectoryAttributes attrs = stream.ReadEnum<AfpFileDirectoryAttributes>();
                            StorageItemAttributes mapped = attrs.MapAttributes(item, volume);
                            StorageItemAttributes newAttrs = item.Attributes;

                            if (attrs.HasFlag(AfpFileDirectoryAttributes.kFPSetClearBit)) {
                                newAttrs |= mapped;
                            } else {
                                newAttrs &= ~mapped;
                            }

                            item.Attributes = newAttrs;

                            break;
                        }
                    case AfpFileDirectoryBitmap.kFPFinderInfoBit: {
                            byte[] data = stream.ReadBytes(32);
                            item.SetFinderInfo(data);
                            break;
                        }

                    case AfpFileDirectoryBitmap.kFPOwnerIDBit: {
                            if (container == null) {
                                return AfpResultCode.FPBitmapErr;
                            }

                            uint ownerId = stream.ReadUInt32();

                            Guid owner;

                            if (volume.Session.UserGroupAssigner.TryGet((int)ownerId, out owner)) {
                                container.SetOwner(owner);
                            }

                            break;
                        }
                    case AfpFileDirectoryBitmap.kFPGroupIDBit: {
                            if (container == null) {
                                return AfpResultCode.FPBitmapErr;
                            }

                            uint groupId = stream.ReadUInt32();
                            Guid group;

                            if (volume.Session.UserGroupAssigner.TryGet((int)groupId, out group)) {
                                container.SetGroup(group);
                            }

                            break;
                        }
                    case AfpFileDirectoryBitmap.kFPAccessRightsBit: {
                            if (container == null) {
                                return AfpResultCode.FPBitmapErr;
                            }

                            AfpAccessRightsBitmap rights = stream.ReadEnum<AfpAccessRightsBitmap>();

                            container.SetAccessRights(rights);

                            break;
                        }
                    case AfpFileDirectoryBitmap.kFPUnixPrivsBit: {
                            AfpUnixPrivileges unixPrivileges = stream.ReadUnixPrivileges();

                            item.SetUnixPermissions(unixPrivileges.permissions);

                            AfpAccessRightsBitmap accessRights = 0;

                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.OwnerRead)) {
                                accessRights |= AfpAccessRightsBitmap.OwnerRead;
                            }
                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.OwnerWrite)) {
                                accessRights |= AfpAccessRightsBitmap.OwnerWrite;
                            }
                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.OwnerExecute)) {
                                accessRights |= AfpAccessRightsBitmap.OwnerExecute;
                            }

                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.GroupRead)) {
                                accessRights |= AfpAccessRightsBitmap.GroupRead;
                            }
                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.GroupWrite)) {
                                accessRights |= AfpAccessRightsBitmap.GroupWrite;
                            }
                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.GroupExecute)) {
                                accessRights |= AfpAccessRightsBitmap.GroupExecute;
                            }

                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.OtherRead)) {
                                accessRights |= AfpAccessRightsBitmap.OtherRead;
                            }
                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.OtherWrite)) {
                                accessRights |= AfpAccessRightsBitmap.OtherWrite;
                            }
                            if (unixPrivileges.permissions.HasFlag(AfpUnixPrivilegePermissions.OtherExecute)) {
                                accessRights |= AfpAccessRightsBitmap.OtherExecute;
                            }

                            item.SetAccessRights(accessRights);

                            Guid group;
                            Guid owner;

                            if (volume.Session.UserGroupAssigner.TryGet((int)unixPrivileges.gid, out group)) {
                                item.SetGroup(group);
                            }

                            if (volume.Session.UserGroupAssigner.TryGet((int)unixPrivileges.uid, out owner)) {
                                item.SetOwner(owner);
                            }

                            break;
                        }
                    default:
                        return AfpResultCode.FPBitmapErr;
                }
            }

            //IStorageFile file = item.AsFile();

            //if (file != null) {
            //    if (!item.Name.StartsWith(AfpHiddenItemPrefix)) {
            //        IStorageFile resourceFile = file.GetResourceForkFile(false);

            //        if (resourceFile != null) {
            //            file.CopyAttributes(resourceFile);
            //        }
            //    }
            //}

            return AfpResultCode.FPNoErr;
        }

        public static void WriteStorageItemTypeByte(this IStorageItem item, AfpStream stream) {
            if (item.Is<IStorageContainer>()) {
                stream.WriteUInt8(1 << 7);
            } else {
                stream.WriteUInt8(0);
            }

            stream.WritePadding();
        }
        public static AfpResultCode WriteStorageItemParameters(this IStorageItem item, AfpStream stream, IAfpVolume volume, AfpFileDirectoryBitmap bitmap, bool includeItemTypeByte = true) {
            if (item == null) {
                throw new ArgumentNullException("item");
            }

            StorageAccessList access = item.GetEffectiveAccess(volume.Session);

            try {
                if (includeItemTypeByte) {
                    item.WriteStorageItemTypeByte(stream);
                }

                stream.BeginMarking();

                try {
                    foreach (AfpFileDirectoryBitmap flag in bitmap.EnumerateFlags()) {
                        switch (flag) {
                            case AfpFileDirectoryBitmap.kFPNodeIDBit:
                                stream.WriteUInt32(volume.GetNode(item));
                                break;
                            case AfpFileDirectoryBitmap.kFPCreateDateBit:
                                stream.WriteMacintoshDate(item.DateCreated);
                                break;
                            case AfpFileDirectoryBitmap.kFPModDateBit:
                                stream.WriteMacintoshDate(item.DateModified);
                                break;
                            case AfpFileDirectoryBitmap.kFPBackupDateBit:
                                stream.WriteMacintoshDate(item.GetBackupDate());
                                break;
                            case AfpFileDirectoryBitmap.kFPAttributeBit: {
                                    AfpFileDirectoryAttributes attrs = item.MapAttributes(volume);
                                    stream.WriteEnum<AfpFileDirectoryAttributes>(attrs);
                                    break;
                                }
                            case AfpFileDirectoryBitmap.kFPParentDirIDBit:
                                stream.WriteUInt32(volume.GetNode(item.Parent));
                                break;
                            case AfpFileDirectoryBitmap.kFPFinderInfoBit:
                                stream.WriteBytes(item.GetFinderInfo());
                                break;
                            case AfpFileDirectoryBitmap.kFPUnixPrivsBit:
                                stream.WriteUnixPrivileges(item.GetUnixPrivileges(volume, volume.Session.User));
                                break;
                            case AfpFileDirectoryBitmap.kFPExtRsrcForkLenBit: {
                                    IStorageFile file = item.DemandFile();
                                    IStorageDataProvider resourceProvider = file.OpenResourceFork(
                                        StorageFileAccess.Read, StorageFileAccess.ReadWrite, StorageFileMode.OpenExisting);
                                    try {
                                        if (resourceProvider == null) {
                                            stream.WriteUInt64(0);
                                        } else {
                                            stream.WriteUInt64((ulong)resourceProvider.Length);
                                        }
                                    } finally {
                                        if (resourceProvider != null) {
                                            resourceProvider.Close();
                                        }
                                    }

                                    break;
                                }
                            case AfpFileDirectoryBitmap.kFPLongNameBit:
                                stream.WriteMark("LongName");
                                break;
                            case AfpFileDirectoryBitmap.kFPShortNameBit:
                                stream.WriteMark("ShortName");
                                break;
                            case AfpFileDirectoryBitmap.kFPUTF8NameBit:
                                stream.WriteMark("UTF8Name");
                                stream.WriteBytes(new byte[4]);
                                break;
                            case AfpFileDirectoryBitmap.OffspringCountOrDataForkLength: {
                                    if (item.Is<IStorageContainer>()) {
                                        IStorageContainer container = item.DemandContainer();
                                        stream.WriteUInt16((ushort)container.Contents.Count);
                                    } else {
                                        IStorageFile file = item.DemandFile();

                                        if (file.Length > uint.MaxValue) {
                                            stream.WriteUInt32(uint.MaxValue);
                                        } else {
                                            stream.WriteUInt32((uint)file.Length);
                                        }
                                    }

                                    break;
                                }
                            case AfpFileDirectoryBitmap.OwnerIdOrResourceForkLength: {
                                    if (item.Is<IStorageContainer>()) {
                                        IStorageContainer container = item.DemandContainer();
                                        stream.WriteUInt32((uint)volume.Session.UserGroupAssigner.Get(volume.Session.User.Id));
                                    } else {
                                        IStorageFile file = item.DemandFile();
                                        IStorageDataProvider resourceProvider = file.OpenResourceFork(
                                            StorageFileAccess.Read, StorageFileAccess.ReadWrite, StorageFileMode.OpenExisting);
                                        try {
                                            if (resourceProvider == null) {
                                                stream.WriteUInt32(0);
                                            } else {
                                                stream.WriteUInt32((uint)resourceProvider.Length);
                                            }
                                        } finally {
                                            if (resourceProvider != null) {
                                                resourceProvider.Close();
                                            }
                                        }
                                    }

                                    break;
                                }
                            case AfpFileDirectoryBitmap.GroupIdOrExtendedDataForkLength: {
                                    if (item.Is<IStorageContainer>()) {
                                        IStorageContainer container = item.DemandContainer();
                                        stream.WriteUInt32((uint)volume.Session.UserGroupAssigner.Get(volume.Session.User.PrimaryGroup));
                                    } else {
                                        IStorageFile file = item.DemandFile();
                                        stream.WriteUInt64((ulong)file.Length);
                                    }

                                    break;
                                }
                            case AfpFileDirectoryBitmap.AccessRightsOrLaunchLimit: {
                                    if (item.Is<IStorageContainer>()) {
                                        IStorageContainer container = item.DemandContainer();
                                        AfpAccessRightsBitmap accessRights = container.GetAccessRights(volume.Session.User);

                                        stream.WriteEnum(accessRights);
                                    } else {
                                        throw new Exception("Launch limit size unknown (can't find in docs).");
                                    }

                                    break;
                                }
                            default:
                                return AfpResultCode.FPBitmapErr;
                        }
                    }

                    foreach (AfpFileDirectoryBitmap flag in bitmap.EnumerateFlags()) {
                        switch (flag) {
                            case AfpFileDirectoryBitmap.kFPLongNameBit:
                                stream.BeginMark("LongName");
                                stream.WritePascalString(item.Name, 32);
                                break;
                            case AfpFileDirectoryBitmap.kFPShortNameBit:
                                stream.BeginMark("ShortName");
                                stream.WritePascalString(item.Name, 12);
                                break;
                            case AfpFileDirectoryBitmap.kFPUTF8NameBit:
                                stream.BeginMark("UTF8Name");
                                stream.WriteUTF8StringWithHint(item.Name);
                                break;
                        }
                    }
                } finally {
                    stream.EndMarking();
                }
            } catch (StorageItemInvalidTypeException) {
                return AfpResultCode.FPBitmapErr;
            }

            return AfpResultCode.FPNoErr;
        }
        public static AfpResultCode WriteStorageItemParameters(this IStorageItem item, AfpStream stream, IAfpVolume volume, AfpFileDirectoryBitmap fileBitmap, AfpFileDirectoryBitmap directoryBitmap, bool includeItemTypeByte = true) {
            AfpFileDirectoryBitmap useBitmap = (item.Is<IStorageContainer>() ? directoryBitmap : fileBitmap);
            return item.WriteStorageItemParameters(stream, volume, useBitmap, includeItemTypeByte);
        }

        public static AfpResultCode WriteStorageItems(this IEnumerable<IStorageItem> items, bool forCatSearch, IAfpVolume volume, AfpFileDirectoryBitmap fileBitmap, AfpFileDirectoryBitmap directoryBitmap, AfpStream stream) {
            var contents = items.ToList();

            stream.WriteEnum<AfpFileDirectoryBitmap>(fileBitmap);
            stream.WriteEnum<AfpFileDirectoryBitmap>(directoryBitmap);

            if (!forCatSearch) {
                stream.WriteInt16((short)contents.Count);

                foreach (IStorageItem item in contents) {
                    AfpStream itemStream = new AfpStream();

                    AfpResultCode result = item.WriteStorageItemParameters(itemStream, volume, fileBitmap, directoryBitmap);
                    if (result != AfpResultCode.FPNoErr) {
                        return result;
                    }

                    itemStream.WritePadding();

                    byte[] itemData = itemStream.ToByteArray();

                    stream.WriteUInt16((ushort)(itemData.Length + 2));

                    stream.WriteBytes(itemData);
                }
            } else {
                stream.WriteInt32((int)contents.Count);

                foreach (IStorageItem item in contents) {
                    AfpStream itemStream = new AfpStream();

                    AfpResultCode result = item.WriteStorageItemParameters(itemStream, volume, fileBitmap, directoryBitmap, false);

                    if (result != AfpResultCode.FPNoErr) {
                        return result;
                    }

                    itemStream.WritePadding();

                    byte[] itemData = itemStream.ToByteArray();

                    stream.WriteUInt16((ushort)itemData.Length);

                    item.WriteStorageItemTypeByte(stream);
                    stream.WriteBytes(itemData);
                }
            }

            return AfpResultCode.FPNoErr;
        }

        public static void WriteVolumeParameters(this IAfpVolume volume, AfpStream stream, AfpVolumeBitmap bitmap) {
            stream.BeginMarking();

            try {
                foreach (AfpVolumeBitmap flag in bitmap.EnumerateFlags()) {
                    switch (flag) {
                        case AfpVolumeBitmap.kFPVolAttributeBit:
                            AfpVolumeAttributesBitmap volBitmap =
                                AfpVolumeAttributesBitmap.kSupportsUTF8Names |
                                AfpVolumeAttributesBitmap.kNoExchangeFiles |
                                /*AfpVolumeAttributesBitmap.kDefaultPrivsFromParent |*/
                                AfpVolumeAttributesBitmap.kSupportsFileIDs |
                                AfpVolumeAttributesBitmap.kSupportsUnixPrivs |
                                AfpVolumeAttributesBitmap.kSupportsExtAttrs |
                                AfpVolumeAttributesBitmap.kSupportsTMLockSteal |
                                AfpVolumeAttributesBitmap.kSupportsCatSearch;

                            stream.WriteEnum(volBitmap);

                            break;
                        case AfpVolumeBitmap.kFPVolSignatureBit:
                            stream.WriteUInt16(AfpConstants.FixedDirectoryIdVolumeSignature);
                            break;
                        case AfpVolumeBitmap.kFPVolCreateDateBit:
                            stream.WriteMacintoshDate(volume.StorageProvider.DateCreated);
                            break;
                        case AfpVolumeBitmap.kFPVolModDateBit:
                            stream.WriteMacintoshDate(volume.StorageProvider.DateModified);
                            break;
                        case AfpVolumeBitmap.kFPVolBackupDateBit:
                            stream.WriteMacintoshDate(volume.StorageProvider.GetBackupDate());
                            break;
                        case AfpVolumeBitmap.kFPVolIDBit:
                            stream.WriteUInt16(volume.Identifier);
                            break;
                        case AfpVolumeBitmap.kFPVolBytesFreeBit:
                            if (volume.StorageProvider.AvailableBytes >= uint.MaxValue) {
                                stream.WriteUInt32(uint.MaxValue);
                            } else {
                                stream.WriteUInt32((uint)volume.StorageProvider.AvailableBytes);
                            }
                            break;
                        case AfpVolumeBitmap.kFPVolBytesTotalBit:
                            if (volume.StorageProvider.TotalBytes >= uint.MaxValue) {
                                stream.WriteUInt32(uint.MaxValue);
                            } else {
                                stream.WriteUInt32((uint)volume.StorageProvider.TotalBytes);
                            }
                            break;
                        case AfpVolumeBitmap.kFPVolNameBit:
                            stream.WriteMark("Name");
                            break;
                        case AfpVolumeBitmap.kFPVolExtBytesFreeBit:
                            stream.WriteUInt64(volume.StorageProvider.AvailableBytes);
                            break;
                        case AfpVolumeBitmap.kFPVolExtBytesTotalBit:
                            stream.WriteUInt64(volume.StorageProvider.TotalBytes);
                            break;
                        case AfpVolumeBitmap.kFPVolBlockSizeBit:
                            stream.WriteUInt32(AfpConstants.DefaultBlockSize);
                            break;
                    }
                }

                foreach (AfpVolumeBitmap flag in bitmap.EnumerateFlags()) {
                    switch (flag) {
                        case AfpVolumeBitmap.kFPVolNameBit:
                            stream.BeginMark("Name");
                            stream.WritePascalString(volume.StorageProvider.Name);
                            break;
                    }
                }
            } finally {
                stream.EndMarking();
            }
        }

        public static StorageItemAttributes MapAttributes(this AfpFileDirectoryAttributes attributes, IStorageItem item, IAfpVolume volume) {
            StorageItemAttributes mapped = StorageItemAttributes.None;

            if (attributes.HasFlag(AfpFileDirectoryAttributes.kFPInvisibleBit)) {
                mapped |= StorageItemAttributes.Hidden;
            }

            return mapped;
        }
        public static AfpFileDirectoryAttributes MapAttributes(this IStorageItem item, IAfpVolume volume) {
            StorageItemAttributes attributes = item.Attributes;

            AfpFileDirectoryAttributes mapped = 0;

            if (attributes.HasFlag(StorageItemAttributes.Hidden)) {
                mapped |= AfpFileDirectoryAttributes.kFPInvisibleBit;
            }

            StorageAccessList access = item.GetEffectiveAccess(volume.Session);

            if (!access.HasRight(StorageAccessRight.Delete)) {
                mapped |= AfpFileDirectoryAttributes.kFPDeleteInhibitBit;
            }

            if (item.Is<IStorageFile>()) {
                if (!access.HasRight(StorageAccessRight.WriteData)) {
                    mapped |= AfpFileDirectoryAttributes.kFPWriteInhibitBit;
                }
            }

            return mapped;
        }

        public static AfpUnixPrivileges GetUnixPrivileges(this IStorageItem item, IAfpVolume volume, IAfpUser user) {
            AfpAccessRightsBitmap accessRights = item.GetAccessRights(user);
            AfpUnixPrivilegePermissions unixPermissions = (item.GetUnixPermissions() ?? 0);

            // Only use the format bits
            unixPermissions = (unixPermissions & AfpUnixPrivilegePermissions.FileTypeMask);

            if (unixPermissions == 0) {
                if (item.Is<IStorageContainer>()) {
                    unixPermissions |= AfpUnixPrivilegePermissions.Directory;
                } else {
                    unixPermissions |= AfpUnixPrivilegePermissions.Regular;
                }
            }

            if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerRead) || accessRights.HasFlag(AfpAccessRightsBitmap.UserRead)) {
                unixPermissions |= AfpUnixPrivilegePermissions.OwnerRead;
            }
            if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerWrite) || accessRights.HasFlag(AfpAccessRightsBitmap.UserWrite)) {
                unixPermissions |= AfpUnixPrivilegePermissions.OwnerWrite;
            }
            if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerExecute) || accessRights.HasFlag(AfpAccessRightsBitmap.UserExecute)) {
                unixPermissions |= AfpUnixPrivilegePermissions.OwnerExecute;
            }

            if (accessRights.HasFlag(AfpAccessRightsBitmap.GroupRead)) {
                unixPermissions |= AfpUnixPrivilegePermissions.GroupRead;
            }
            if (accessRights.HasFlag(AfpAccessRightsBitmap.GroupWrite)) {
                unixPermissions |= AfpUnixPrivilegePermissions.GroupWrite;
            }
            if (accessRights.HasFlag(AfpAccessRightsBitmap.GroupExecute)) {
                unixPermissions |= AfpUnixPrivilegePermissions.GroupExecute;
            }

            if (accessRights.HasFlag(AfpAccessRightsBitmap.OtherRead)) {
                unixPermissions |= AfpUnixPrivilegePermissions.OtherRead;
            }
            if (accessRights.HasFlag(AfpAccessRightsBitmap.OtherWrite)) {
                unixPermissions |= AfpUnixPrivilegePermissions.OtherWrite;
            }
            if (accessRights.HasFlag(AfpAccessRightsBitmap.OtherExecute)) {
                unixPermissions |= AfpUnixPrivilegePermissions.OtherExecute;
            }

            Guid ownerId = (item.GetOwner() ?? Guid.Empty);
            Guid groupId = (item.GetGroup() ?? Guid.Empty);

            AfpUnixPrivileges privileges = new AfpUnixPrivileges() {
                uid = (uint)volume.Session.UserGroupAssigner.Get(ownerId),
                gid = (uint)volume.Session.UserGroupAssigner.Get(groupId),
                accessRights = accessRights,
                permissions = unixPermissions
            };

            return privileges;

            //IShareServer server = (IShareServer)volume.Session.Server;
            //IShareSession session = (IShareSession)volume.Session;

            //AfpAccessRightsBitmap defaultAccessRights =
            //    (AfpAccessRightsBitmap.UserReadWriteExecute | AfpAccessRightsBitmap.OwnerReadWriteExecute | AfpAccessRightsBitmap.GroupReadWriteExecute | AfpAccessRightsBitmap.OtherReadWriteExecute | AfpAccessRightsBitmap.UserIsOwner);

            //Guid ownerId = (item.GetOwner() ?? Guid.Empty);
            //Guid groupId = (item.GetGroup() ?? Guid.Empty);

            //if (ownerId == Guid.Empty) {
            //    if (session.User != null) {
            //        ownerId = session.User.Identifier;

            //        if (groupId == Guid.Empty) {
            //            groupId = session.User.PrimaryGroup;
            //        }
            //    }
            //}

            //AfpAccessRightsBitmap accessRights = (item.GetAccessRights() ?? defaultAccessRights);

            //if (session.User != null && ownerId == session.User.Identifier) {
            //    accessRights |= AfpAccessRightsBitmap.UserIsOwner;
            //    if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerRead)) {
            //        accessRights |= AfpAccessRightsBitmap.UserRead;
            //    }
            //    if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerWrite)) {
            //        accessRights |= AfpAccessRightsBitmap.UserWrite;
            //    }
            //    if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerExecute)) {
            //        accessRights |= AfpAccessRightsBitmap.UserExecute;
            //    }
            //}

            //AfpUnixPrivilegePermissions defaultPermissions =
            //    (AfpUnixPrivilegePermissions.OtherReadWriteExecute | AfpUnixPrivilegePermissions.OwnerReadWriteExecute | AfpUnixPrivilegePermissions.GroupReadWriteExecute);
            //if (item.Type == StorageItemType.File) {
            //    defaultPermissions |= AfpUnixPrivilegePermissions.Regular;
            //} else {
            //    defaultPermissions |= AfpUnixPrivilegePermissions.Directory;
            //}

            //AfpUnixPrivilegePermissions permissions = (item.GetUnixPermissions() ?? defaultPermissions);

            //AfpUnixPrivileges p = new AfpUnixPrivileges() {
            //    uid = (uint)volume.Session.AssignIdToUserOrGroup(ownerId),
            //    gid = (uint)volume.Session.AssignIdToUserOrGroup(groupId),
            //    accessRights = accessRights,
            //    permissions = permissions
            //};

            //return p;
        }

        public static StorageAccessList GetEffectiveAccess(this IStorageItem item, IAfpSession session) {
            return (session.GetAccess(item) + item.AvailableAccess);
        }

        public static IEnumerable<SearchParameter> ReadSearchParameters(
            this IAfpVolume volume,
            AfpFileDirectoryBitmap fileBitmap,
            AfpFileDirectoryBitmap directoryBitmap,
            AfpFileDirectoryBitmap searchBitmap,
            bool partialMatch,
            AfpStream spec1Stream,
            AfpStream spec2Stream) {

            IDictionary<AfpFileDirectoryBitmap, object> specs1 = volume.ReadSearchSpecification(fileBitmap, directoryBitmap, searchBitmap, spec1Stream);
            IDictionary<AfpFileDirectoryBitmap, object> specs2 = volume.ReadSearchSpecification(fileBitmap, directoryBitmap, searchBitmap, spec2Stream);

            foreach (var specPair in specs1) {
                object spec1Value = specPair.Value;
                object spec2Value = specs2[specPair.Key];
                AfpFileDirectoryBitmap flag = specPair.Key;

                switch (flag) {
                    case AfpFileDirectoryBitmap.kFPLongNameBit:
                    case AfpFileDirectoryBitmap.kFPUTF8NameBit:
                        yield return new NameSearchParameter((string)spec1Value, !partialMatch);
                        break;
                    case AfpFileDirectoryBitmap.kFPCreateDateBit:
                        yield return new DateCreatedSearchParameter(
                            (DateTime)spec1Value, (DateTime)spec2Value);
                        break;
                    case AfpFileDirectoryBitmap.kFPModDateBit:
                        yield return new DateModifiedSearchParameter(
                            (DateTime)spec1Value, (DateTime)spec2Value);
                        break;
                }
            }
        }

        private static IDictionary<AfpFileDirectoryBitmap, object> ReadSearchSpecification(this IAfpVolume volume, AfpFileDirectoryBitmap fileBitmap, AfpFileDirectoryBitmap directoryBitmap, AfpFileDirectoryBitmap searchBitmap, AfpStream specStream) {
            Dictionary<AfpFileDirectoryBitmap, object> specs = new Dictionary<AfpFileDirectoryBitmap, object>();

            specStream.BeginMarking();
            try {
                foreach (AfpFileDirectoryBitmap flag in searchBitmap.EnumerateFlags()) {
                    object specValue = null;

                    switch (flag) {
                        case AfpFileDirectoryBitmap.kFPLongNameBit:
                            specStream.ReadMark("LongName");
                            break;
                        case AfpFileDirectoryBitmap.kFPUTF8NameBit:
                            specStream.ReadMark("UTF8Name");
                            break;
                        case AfpFileDirectoryBitmap.kFPFinderInfoBit:
                            specValue = specStream.ReadBytes(16);
                            break;
                        case AfpFileDirectoryBitmap.OffspringCountOrDataForkLength: {
                                if (fileBitmap == 0) {
                                    // Offspring count
                                    specValue = specStream.ReadUInt16();
                                } else if (directoryBitmap == 0) {
                                    specValue = specStream.ReadUInt32();
                                } else {
                                    throw new Exception();
                                }

                                break;
                            }
                        case AfpFileDirectoryBitmap.kFPRsrcForkLenBit:
                            specValue = specStream.ReadUInt32();
                            break;
                        case AfpFileDirectoryBitmap.kFPExtDataForkLenBit:
                        case AfpFileDirectoryBitmap.kFPExtRsrcForkLenBit:
                            specValue = specStream.ReadUInt64();
                            break;
                        case AfpFileDirectoryBitmap.kFPBackupDateBit:
                        case AfpFileDirectoryBitmap.kFPModDateBit:
                        case AfpFileDirectoryBitmap.kFPCreateDateBit:
                            specValue = specStream.ReadMacintoshDate();
                            break;
                        case AfpFileDirectoryBitmap.kFPParentDirIDBit:
                            specValue = specStream.ReadUInt32();
                            break;
                        default:
                            throw new InvalidOperationException("Unrecognized flag");
                    }

                    if (specValue != null) {
                        specs[flag] = specValue;
                    }
                }

                // Read variable length params
                foreach (AfpFileDirectoryBitmap flag in searchBitmap.EnumerateFlags()) {
                    object specValue = null;

                    switch (flag) {
                        case AfpFileDirectoryBitmap.kFPLongNameBit:
                            specStream.BeginReadingMark("LongName");
                            specValue = specStream.ReadPascalString();
                            break;
                        case AfpFileDirectoryBitmap.kFPUTF8NameBit:
                            specStream.BeginReadingMark("UTF8Name");
                            specValue = specStream.ReadUTF8StringWithHint();
                            break;
                    }

                    if (specValue != null) {
                        specs[flag] = specValue;
                    }
                }
            } finally {
                specStream.EndMarking();
            }

            return specs;
        }
    }
}
